package com.bowen.demo.demo005;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * <h3>java-study</h3>
 * <p>ReflectClass </p>
 * @author : zhang.bw
 * @date : 2021-03-19 18:10
 **/
@Slf4j
public class ReflectClass {

	// 创建对象
	public static void reflectNewInstance() {
		try {
			// 根据类名返回类的对象
			Class<?> classBook = Class.forName("com.bowen.demo.demo005.Book");
			// 创建类的实例
			Object objectBook = classBook.newInstance();
			Book book = (Book) objectBook;
			book.setName("猿码天地");
			book.setAuthor("猿人");
			System.out.println(book.toString());
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	// 反射私有构造方法
	public static void reflectPrivateConstructor() {
		try {
			Class<?> classBook = Class.forName("com.bowen.demo.demo005.Book");
			Constructor<?> declaredConstructorBook = classBook.getDeclaredConstructor(String.class,String.class);
			declaredConstructorBook.setAccessible(true);
			Object objectBook = declaredConstructorBook.newInstance("猿码天地","猿人");
			Book book = (Book) objectBook;
			System.out.println(book.toString());
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	// 反射私有属性
	public static void reflectPrivateField() {
		try {
			Class<?> classBook = Class.forName("com.bowen.demo.demo005.Book");
			Object objectBook = classBook.newInstance();
			Field fieldTag = classBook.getDeclaredField("TAG");
			fieldTag.setAccessible(true);
			String tag = (String) fieldTag.get(objectBook);
			System.out.println(tag);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	// 反射私有方法
	public static void reflectPrivateMethod() {
		try {
			Class<?> classBook = Class.forName("com.bowen.demo.demo005.Book");
			Method methodBook = classBook.getDeclaredMethod("declaredMethod",int.class);
			methodBook.setAccessible(true);
			Object objectBook = classBook.newInstance();
			String string = (String) methodBook.invoke(objectBook,0);
			System.out.println(string);
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	/**
	 * 将对象转换为Map键值对
	 * @param obj
	 * @return
	 */
	public static Map<String, Object> reflectKeyAndValue(Object obj) {
		Map<String, Object> result = new HashMap<String, Object>();
		// 得到类对象
		Class cla = (Class) obj.getClass();
		/* 得到类中的所有属性集合 */
		Field[] fs = cla.getDeclaredFields();
		for (int i = 0; i < fs.length; i++) {
			Field f = fs[i];
			// 设置哪些属性是可以访问的
			f.setAccessible(true);
			Object val = new Object();
			try {
				val = f.get(obj);
				// 设置键值
				result.put(f.getName(), val);
			} catch (IllegalArgumentException e) {
				log.error("非法参数异常", e);
			} catch (IllegalAccessException e) {
				log.error("非法访问异常", e);
			}
		}
		log.debug("获取对象的所有键值: {}", result);
		return result;
	}

	/**
	 * 使用 Java 反射获取 Class 对象（三种方式）
	 *
	 * 方式1：通过 Class 类的静态方法获取 Class 类对象 (推荐)
	 * 方式2：因为所有类都继承 Object 类。因而可通过调用 Object 类中的 getClass 方法来获取
	 * 方式3：任何数据类型（包括基本数据类型）都有一个“静态”的 class 属性
	 * @throws Exception
	 */
	public static void test1() throws Exception {
		Class<?> class1 = null;
		Class<?> class2 = null;
		Class<?> class3 = null;
		class1 = Class.forName("com.bowen.demo.demo005.Book");
		class2 = new Book().getClass();
		class3 = Book.class;
		System.out.println("类名称 1：  " + class1.getName());
		System.out.println("类名称 2：  " + class2.getName());
		System.out.println("类名称 3：  " + class3.getName());
	}

	/**
	 * 使用 Java 反射获取一个对象的父类和实现的接口
	 * @throws Exception
	 */
	public static void test2() throws Exception{
		Class<?> clazz = Class.forName("com.bowen.demo.demo005.Book");
		// 取得父类
		Class<?> parentClass = clazz.getSuperclass();
		System.out.println("父类为：" + parentClass.getName());
		// 获取所有的接口
		Class<?> intes[] = clazz.getInterfaces();
		System.out.println("实现的接口有：");
		for (int i = 0; i < intes.length; i++) {
			System.out.println((i + 1) + "：" + intes[i].getName());
		}
	}

	/**
	 * 使用 Java 反射获取某个类的全部构造函数并调用私有构造方法
	 */
	public static void test3() throws Exception{

		//1.加载Class对象
		Class clazz = Class.forName("com.bowen.demo.demo005.Student");
		//2.获取所有公有构造方法
		System.out.println("所有公有构造方法");
		Constructor[] conArray = clazz.getConstructors();
		for(Constructor c : conArray){
			System.out.println(c);
		}

		System.out.println("所有的构造方法(包括：私有、受保护、默认、公有)");
		conArray = clazz.getDeclaredConstructors();
		for(Constructor c : conArray){
			System.out.println(c);
		}

		System.out.println("获取公有、无参的构造方法");
		//1、因为是无参的构造方法所以类型是一个null,不写也可以：这里需要的是一个参数的***类型***，
		//2、返回的是描述这个无参构造函数的类对象。
		Constructor con = clazz.getConstructor(null);
		System.out.println("con = " + con);
		//调用构造方法
		Object obj = con.newInstance();
		System.out.println("obj = " + obj);

		System.out.println("获取私有构造方法，并调用");
		con = clazz.getDeclaredConstructor(int.class);
		System.out.println(con);
		//调用构造方法
		con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
		obj = con.newInstance(26);

	}

	/**
	 * 使用 Java 反射获取某个类的全部属性
	 */
	public static void test4() throws Exception{

		//1.获取Class对象
		Class stuClass = Class.forName("com.bowen.demo.demo005.Student");
		//2.获取字段
		System.out.println("获取所有公有的字段");
		Field[] fieldArray = stuClass.getFields();
		for(Field f : fieldArray){
			System.out.println(f);
		}
		System.out.println();
		System.out.println("获取所有的字段(包括私有、受保护、默认的)");
		fieldArray = stuClass.getDeclaredFields();
		for(Field f : fieldArray){
			System.out.println(f);
		}
		System.out.println();
		System.out.println("获取公有字段并调用");
		Field f = stuClass.getField("name");
		System.out.println(f);
		//获取一个对象
		Object obj = stuClass.getConstructor().newInstance();
		f.set(obj, "laughitover");
		//验证
		Student stu = (Student)obj;
		System.out.println("验证姓名：" + stu.name);

		System.out.println();
		System.out.println("获取私有字段并调用");
		f = stuClass.getDeclaredField("phoneNum");
		System.out.println(f);
		f.setAccessible(true);//暴力反射，解除私有限定
		f.set(obj, "13812345678");
		System.out.println("验证电话：" + stu);

	}

	/**
	 * 使用 Java 反射获取某个类的全部方法
	 */
	public static void test5() throws Exception{

		//1.获取Class对象
		Class stuClass = Class.forName("com.bowen.demo.demo005.Student");
		//2.获取所有公有方法
		System.out.println("获取所有的”公有“方法");
		stuClass.getMethods();
		Method[] methodArray = stuClass.getMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		System.out.println("获取所有的方法，包括私有的");
		methodArray = stuClass.getDeclaredMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		System.out.println("获取公有的show1()方法");
		Method m = stuClass.getMethod("show1", String.class);
		System.out.println(m);
		//实例化一个Student对象
		Object obj = stuClass.getConstructor().newInstance();
		m.invoke(obj, "laughitover");

		System.out.println("获取私有的show4()方法");
		m = stuClass.getDeclaredMethod("show4", int.class);
		System.out.println(m);
		m.setAccessible(true);//解除私有限定
		Object result = m.invoke(obj, 20);//需要两个参数，一个是要调用的对象（获取有反射），一个是实参
		System.out.println("返回值：" + result);

	}

	/**
	 *  使用 Java 反射调用某个类的方法
	 *  Java 反射获取 Class 对象并使用invoke()方法调用方法 reflect1() 和 reflect2()
	 */
	public static void test6() throws Exception{
		Class<?> clazz = Class.forName("com.bowen.demo.demo005.ReflectClass");
		Method method = clazz.getMethod("reflect1");
		method.invoke(clazz.newInstance());
		method = clazz.getMethod("reflect2", int.class, String.class);
		method.invoke(clazz.newInstance(), 20, "张三");
	}
	public void reflect1() {
		System.out.println("Java 反射机制 - 调用某个类的方法1.");
	}
	public void reflect2(int age, String name) {
		System.out.println("Java 反射机制 - 调用某个类的方法2.");
		System.out.println("age -> " + age + ". name -> " + name);
	}

	/**
	 *  通过反射越过泛型检查
	 *  泛型作用在编译期，编译过后泛型擦除（消失掉）。所以是可以通过反射越过泛型检查的
	 */
	public static void test7() throws Exception{
		ArrayList<Integer> list = new ArrayList<Integer>();
		list.add(100);
		list.add(200);
		Method method = list.getClass().getMethod("add", Object.class);
		method.invoke(list, "Java反射机制实例。");
		//遍历集合
		for(Object obj : list){
			System.out.println(obj);
		}
	}

	/**
	 *  通过反射机制获得数组信息并修改数组的大小和值
	 *  通过反射机制分别修改int和String类型的数组的大小并修改int数组的第一个值
	 */
	public static void test8() throws Exception{
		int[] temp = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
		int[] newTemp = (int[]) arrayInc(temp, 15);
		print(newTemp);
		Array.set(newTemp, 0, 100);
		System.out.println("修改之后数组第一个元素为： " + Array.get(newTemp, 0));
		print(newTemp);
		String[] atr = { "a", "b", "c" };
		String[] str1 = (String[]) arrayInc(atr, 8);
		print(str1);
	}
	// 修改数组大小
	public static Object arrayInc(Object obj, int len) {
		Class<?> arr = obj.getClass().getComponentType();
		Object newArr = Array.newInstance(arr, len);
		int co = Array.getLength(obj);
		System.arraycopy(obj, 0, newArr, 0, co);
		return newArr;
	}
	// 打印
	public static void print(Object obj) {
		Class<?> c = obj.getClass();
		if (!c.isArray()) {
			return;
		}
		Class<?> arr = obj.getClass().getComponentType();
		System.out.println("数组类型： " + arr.getName());
		System.out.println("数组长度为： " + Array.getLength(obj));
		for (int i = 0; i < Array.getLength(obj); i++) {
			System.out.print(Array.get(obj, i) + " ");
		}
		System.out.println();
	}

	/**
	 * 通过 Java 反射机制获取 ClassLoader 类加载器
	 */
	public static void test9() throws Exception{

		//1、获取一个系统的类加载器
		ClassLoader classLoader = ClassLoader.getSystemClassLoader();
		System.out.println("系统的类加载器-->" + classLoader);

		//2、获取系统类加载器的父类加载器(扩展类加载器（extensions classLoader）)
		classLoader = classLoader.getParent();
		System.out.println("扩展类加载器-->" + classLoader);

		//3、获取扩展类加载器的父类加载器
		//输出为Null,引导类加载器无法被Java程序直接引用
		classLoader = classLoader.getParent();
		System.out.println("启动类加载器-->" + classLoader);

		//4、测试当前类由哪个类加载器进行加载 ,结果就是系统的类加载器
		classLoader = Class.forName("com.channelsoft.ssm.controller.ReflectTest").getClassLoader();
		System.out.println("当前类由哪个类加载器进行加载-->"+classLoader);

		//5、测试JDK提供的Object类由哪个类加载器负责加载的
		classLoader = Class.forName("java.lang.Object").getClassLoader();
		System.out.println("JDK提供的Object类由哪个类加载器加载-->" + classLoader);

	}

	public static void main(String[] args) {
		try {
			// 创建对象
			ReflectClass.reflectNewInstance();

			// 反射私有构造方法
			ReflectClass.reflectPrivateConstructor();

			// 反射私有属性
			ReflectClass.reflectPrivateField();

			// 反射私有方法
			ReflectClass.reflectPrivateMethod();

			// 将对象转换为Map键值对
			Book book = new Book();
			book.setName("猿码天地");
			book.setAuthor("猿人");
			Map map = ReflectClass.reflectKeyAndValue(book);
			System.out.println(map);

			// 使用 Java 反射获取 Class 对象（三种方式）
			ReflectClass.test1();

			// 使用 Java 反射获取一个对象的父类和实现的接口
			ReflectClass.test2();

			// 使用 Java 反射获取某个类的全部构造函数并调用私有构造方法
			ReflectClass.test3();

			// 使用 Java 反射获取某个类的全部属性
			ReflectClass.test4();

			// 使用 Java 反射获取某个类的全部方法
			ReflectClass.test5();

			// 使用 Java 反射调用某个类的方法
			ReflectClass.test6();

			// 通过反射越过泛型检查
			ReflectClass.test7();

			// 通过反射机制获得数组信息并修改数组的大小和值
			ReflectClass.test8();

			// 通过 Java 反射机制获取 ClassLoader 类加载器
			ReflectClass.test9();

		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
}